# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)

set(KERNEL_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
set(KERNEL_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})

file(MAKE_DIRECTORY ${KERNEL_BINARY_DIR}/include/generated)

define_property(GLOBAL PROPERTY KERNEL_BUILTIN_LIBS
  BRIEF_DOCS "Global list of all kernel builtin CMake libs that should be linked in"
  FULL_DOCS  "Global list of all kernel builtin CMake libs that should be linked in.
kernel_library() appends libs to this list.")
set_property(GLOBAL PROPERTY KERNEL_BUILTIN_LIBS "")

define_property(GLOBAL PROPERTY KERNEL_INTERFACE_LIBS
  BRIEF_DOCS "Global list of all kernel interface CMake libs that should be linked in"
  FULL_DOCS  "Global list of all kernel interface CMake libs that should be linked in.
kernel_interface_library() appends libs to this list.")
set_property(GLOBAL PROPERTY KERNEL_INTERFACE_LIBS "")

define_property(GLOBAL PROPERTY KERNEL_IMPORT_LIBS
  BRIEF_DOCS "Global list of all kernel import CMake libs that should be linked in"
  FULL_DOCS  "Global list of all kernel import CMake libs that should be linked in.
kernel_import_library(lib_name) appends libs to this list.")
set_property(GLOBAL PROPERTY KERNEL_IMPORT_LIBS "")

project(seminix VERSION ${SEMINIX_VERSION})

enable_language(C CXX ASM)

set(KERNEL_NAME "kernel")

set(KERNEL_ELF_NAME ${KERNEL_NAME}.elf)
set(KERNEL_BIN_NAME ${KERNEL_NAME}.bin)
set(KERNEL_MAP_NAME ${KERNEL_NAME}.map)

set(KERNEL_FINAL_EXECUTABLE   kernel_final)

target_ld_options(kernel_interface INTERFACE -Wl,--build-id -Wl,-z)

target_include_directories(
  kernel_interface
  INTERFACE
  include arch/${ARCH}/include
  ${KERNEL_BINARY_DIR}/include
  ${SEMINIX_SOURCE_DIR}/libseminix/include/uapi
  ${SEMINIX_SOURCE_DIR}/libseminix/arch/${ARCH}/include/uapi
)

kernel_compile_definitions(__KERNEL__)

kernel_cc_options(-fmacro-prefix-map=${KERNEL_SOURCE_DIR}=kernel)

set(KERNEL_TEXT_OFFSET  CONFIG_TEXT_OFFSET)

set(GLOBAL PROPERTY KERNEL_POST_BUILD_COMMANDS "")
set(GLOBAL PROPERTY KERNEL_POST_BUILD_BYPRODUCTS "")

kernel_import_library(utils)

add_subdirectory(arch/${ARCH})

set(ASM_OFFSETS_LIB   asm_offsets)
# Set some phony targets to collect dependencies
set(ASM_OFFSETS_H_TARGET     asm_offsets_h)
set(ASM_OFFSETS_C_PATH ${KERNEL_SOURCE_DIR}/arch/${ARCH}/kernel/asm-offsets.c)
set(ASM_OFFSETS_H_PATH ${KERNEL_BINARY_DIR}/include/generated/asm-offsets.h)

add_library(${ASM_OFFSETS_LIB} OBJECT ${ASM_OFFSETS_C_PATH})
target_link_libraries(${ASM_OFFSETS_LIB} kernel_interface)
set_target_properties(${ASM_OFFSETS_LIB} PROPERTIES COMPILE_OPTIONS "-S")

add_custom_command(
  OUTPUT ${ASM_OFFSETS_H_PATH}
  COMMAND ${PYTHON_EXECUTABLE} ${SEMINIX_KERNEL_DIR}/scripts/asm_offset_h.py
  -n "__ASM_OFFSETS_H_"
  -i $<TARGET_OBJECTS:${ASM_OFFSETS_LIB}>
  -o ${ASM_OFFSETS_H_PATH}
  DEPENDS
  ${ASM_OFFSETS_LIB}
  $<TARGET_OBJECTS:${ASM_OFFSETS_LIB}>
  WORKING_DIRECTORY
  ${KERNEL_BINARY_DIR}
)
add_custom_target(${ASM_OFFSETS_H_TARGET} DEPENDS ${ASM_OFFSETS_H_PATH})

set(TIMECONST_BC_PATH ${KERNEL_SOURCE_DIR}/kernel/time/timeconst.bc)
set(TIMECONST_H_PATH ${KERNEL_BINARY_DIR}/include/generated/timeconst.h)

add_custom_command(
  OUTPUT ${TIMECONST_H_PATH}
  COMMAND ${SHELL} ${SEMINIX_KERNEL_DIR}/scripts/timeconst.sh
  ${CONFIG_HZ}
  ${BC}
  ${TIMECONST_BC_PATH}
  ${TIMECONST_H_PATH}
  WORKING_DIRECTORY
  ${KERNEL_BINARY_DIR}
)
add_custom_target(timeconst_h DEPENDS ${TIMECONST_H_PATH})

# 生成 compile.h
set(COMPILE_H_TARGET           compile_h)
set(COMPILE_H_PATH ${KERNEL_BINARY_DIR}/include/generated/compile.h)

if(CONFIG_SMP)
  set(SMP_STRING "SMP")
else()
  set(SMP_STRING "UP")
endif()

add_custom_target(${COMPILE_H_TARGET}
  COMMAND ${SHELL} ${SEMINIX_KERNEL_DIR}/scripts/mkcompile_h
  ${COMPILE_H_PATH}
  ${UTS_MACHINE}
  ${SMP_STRING}
  ${CMAKE_C_COMPILER}
  ${KERNEL_BINARY_DIR}
  ${APPLICATION_BINARY_DIR}
  BYPRODUCTS ${COMPILE_H_PATH}
  WORKING_DIRECTORY
  ${KERNEL_BINARY_DIR}
)

add_custom_target(kernel_generated_headers)
add_dependencies(kernel_generated_headers
  asm_offsets_h
  timeconst_h
)
add_dependencies(asm_offsets_h timeconst_h)

add_subdirectory(init)
add_subdirectory(kernel)
add_subdirectory(drivers)
add_subdirectory(mm)
add_subdirectory(posix)
add_subdirectory(lib)

# Read list content
get_property(KERNEL_BUILTIN_LIBS_PROPERTY GLOBAL PROPERTY KERNEL_BUILTIN_LIBS)
# Read list content
get_property(KERNEL_INTERFACE_LIBS_PROPERTY GLOBAL PROPERTY KERNEL_INTERFACE_LIBS)
# Read list content
get_property(KERNEL_IMPORT_LIBS_PROPERTY GLOBAL PROPERTY KERNEL_IMPORT_LIBS)

add_library(version ${KERNEL_SOURCE_DIR}/init/version.c)
set_target_properties(
	version
	PROPERTIES
  COMPILE_DEFINITIONS __KERNEL_VERSION__
)
add_dependencies(version ${COMPILE_H_TARGET})
target_link_libraries(version PRIVATE kernel_interface)

foreach(dep_lib ${KERNEL_BUILTIN_LIBS_PROPERTY})
  add_dependencies(${dep_lib} kernel_generated_headers)
  add_dependencies(${COMPILE_H_TARGET} ${dep_lib})
endforeach()
foreach(dep_lib ${KERNEL_INTERFACE_LIBS_PROPERTY})
  add_dependencies(${dep_lib} kernel_generated_headers)
  add_dependencies(${COMPILE_H_TARGET} ${dep_lib})
endforeach()
foreach(dep_lib ${KERNEL_IMPORT_LIBS_PROPERTY})
  add_dependencies(${COMPILE_H_TARGET} ${dep_lib})
endforeach()

get_property(KERNEL_LINKER_SCRIPT GLOBAL PROPERTY KERNEL_LINKER_SCRIPT_S)
if(NOT EXISTS ${KERNEL_LINKER_SCRIPT})
  message(FATAL_ERROR "Could not find linker script: '${CMAKE_CURRENT_SOURCE_DIR}'. ${CMAKE_CURRENT_LIST_FILE} Corrupted configuration?")
endif()

configure_kernel_linker_script(
  ${KERNEL_BINARY_DIR}/linker.lds
  "-D__ASSEMBLY__;-DLINKER_SCRIPT;-U${ARCH};-D__KERNEL__;-DTEXT_OFFSET=${KERNEL_TEXT_OFFSET}"
  kernel_generated_headers
)

add_custom_target(
  linker_kernel_prebuilt_script_target
  DEPENDS
  ${KERNEL_BINARY_DIR}/linker.lds
)

set_source_files_properties(
  ${KERNEL_SOURCE_DIR}/misc/empty.c
  PROPERTIES COMPILE_OPTIONS "-Wno-empty-translation-unit"
)
add_executable(${KERNEL_FINAL_EXECUTABLE} ${SEMINIX_KERNEL_DIR}/misc/empty.c)
toolchain_ld_link_kernel_elf(
  TARGET_ELF            ${KERNEL_FINAL_EXECUTABLE}
  OUTPUT_MAP            ${KERNEL_BINARY_DIR}/${KERNEL_FINAL_EXECUTABLE}.map
  LINKER_SCRIPT         ${KERNEL_BINARY_DIR}/linker.lds
)
set_property(TARGET   ${KERNEL_FINAL_EXECUTABLE} PROPERTY LINK_DEPENDS ${KERNEL_BINARY_DIR}/linker.lds)
add_dependencies(${KERNEL_FINAL_EXECUTABLE} linker_kernel_prebuilt_script_target)
set_target_properties(${KERNEL_FINAL_EXECUTABLE} PROPERTIES OUTPUT_NAME ${KERNEL_NAME})

set_property(GLOBAL APPEND
  PROPERTY KERNEL_POST_BUILD_COMMANDS
  COMMAND
  ${CMAKE_COMMAND} -E rename ${KERNEL_FINAL_EXECUTABLE}.map ${KERNEL_MAP_NAME}
)
set_property(GLOBAL APPEND
  PROPERTY KERNEL_POST_BUILD_BYPRODUCTS
  ${KERNEL_MAP_NAME}
)

set_property(GLOBAL APPEND
  PROPERTY KERNEL_POST_BUILD_COMMANDS
  COMMAND
  ${SHELL}
  ${SEMINIX_KERNEL_DIR}/scripts/mksysmap
  ${CMAKE_NM}
  ${KERNEL_ELF_NAME}
  ${GREP}
  ${KERNEL_BINARY_DIR}/System.map
)
set_property(GLOBAL APPEND
  PROPERTY KERNEL_POST_BUILD_BYPRODUCTS
  ${KERNEL_BINARY_DIR}/System.map
)

set_property(GLOBAL APPEND
	PROPERTY KERNEL_POST_BUILD_COMMANDS
	COMMAND
	${SHELL} ${SEMINIX_KERNEL_DIR}/scripts/gen_version
	${KERNEL_BINARY_DIR}
)
set_property(GLOBAL APPEND
	PROPERTY KERNEL_POST_BUILD_BYPRODUCTS
	${KERNEL_BINARY_DIR}/.version
)

get_property(post_build_commands GLOBAL PROPERTY KERNEL_POST_BUILD_COMMANDS)
get_property(post_build_byproducts GLOBAL PROPERTY KERNEL_POST_BUILD_BYPRODUCTS)

add_custom_command(
  TARGET ${KERNEL_FINAL_EXECUTABLE}
  POST_BUILD
  ${post_build_commands}
  BYPRODUCTS
  ${post_build_byproducts}
  COMMAND_EXPAND_LISTS
  # NB: COMMENT only works for some CMake-Generators
)

set_property(GLOBAL PROPERTY CORE_IMAGE_BINARY kernelbin)
set_property(GLOBAL PROPERTY CORE_IMAGE_BINARY_FILE ${KERNEL_BINARY_DIR}/${KERNEL_BIN_NAME})
